package com.huangfu.learning.javabase.java.pattern.structure.adapter;

/**
 * Created with IntelliJ IDEA.
 * Author: huangfu.daohui
 * Date: 2018/8/7
 * Time: 15:56
 * Version: 1.0
 * Description: 适配器模式
 * https://www.cnblogs.com/songyaqi/p/4805820.html
 * https://www.cnblogs.com/V1haoge/p/6479118.html
 * http://www.runoob.com/design-pattern/adapter-pattern.html
 * <p>
 * 三个角色：
 * 1：Target(目标抽象类)：目标抽象类定义客户所需的接口，可以是一个抽象类或接口，也可以是具体类。在类适配器中，由于C#语言不支持多重继承，所以它只能是接口。
 * 2：Adapter(适配器类)：它可以调用另一个接口，作为一个转换器，对Adaptee和Target进行适配。它是适配器模式的核心。
 * 3：Adaptee(适配者类)：适配者即被适配的角色，它定义了一个已经存在的接口，这个接口需要适配，适配者类包好了客户希望的业务方法。
 * <p>
 * 优点：
 * 1：将目标类和适配者类解耦，通过引入一个适配器类来重用现有的适配者类，无需修改原有结构。
 * 2：增加了类的透明性和复用性，将具体的业务实现过程封装在适配者类中，对于客户端类而言是透明的，而且提高了适配者的复用性，同一适配者类可以在多个不同的系统中复用。
 * 3：灵活性和扩展性都非常好，通过使用配置文件，可以很方便的更换适配器，也可以在不修改原有代码的基础上 增加新的适配器，完全复合开闭原则。
 * <p>
 * 缺点：
 * 1：一次最多只能适配一个适配者类，不能同时适配多个适配者。
 * 2：适配者类不能为最终类
 * 3：目标抽象类只能为接口，不能为类，其使用有一定的局限性。
 * <p>
 * 适配器模式的适用环境
 * 系统需要使用一些现有的类，而这些类的接口不符合系统的需要，甚至没有这些类的源代码
 * 创建一个可以重复使用的类，用于和一些彼此之间没有太大关联的类，包括一些可能在将来引进的类一起工作
 * 适配器继承或依赖已有的对象，实现想要的目标接口。
 * <p>
 * 应用场景：
 * 我们有一个 MediaPlayer 接口和一个实现了 MediaPlayer 接口的实体类 AudioPlayer。默认情况下，AudioPlayer 可以播放 mp3 格式的音频文件。
 * 我们还有另一个接口 AdvancedMediaPlayer 和实现了 AdvancedMediaPlayer 接口的实体类。该类可以播放 vlc 和 mp4 格式的文件。
 * 我们想要让 AudioPlayer 播放其他格式的音频文件。为了实现这个功能，我们需要创建一个实现了 MediaPlayer 接口的适配器类 MediaAdapter，并使用 AdvancedMediaPlayer 对象来播放所需的格式。
 * AudioPlayer 使用适配器类 MediaAdapter 传递所需的音频类型，不需要知道能播放所需格式音频的实际类。AdapterPatternDemo，我们的演示类使用 AudioPlayer 类来播放各种格式。
 **/
public class AdapterPattern {

    // 测试
    public static void main(String[] args) {
        AudioPlayer audioPlayer = new AudioPlayer();
        audioPlayer.play("mp3", "beyond the horizon.mp3");
        audioPlayer.play("mp4", "alone.mp4");
        audioPlayer.play("vlc", "far far away.vlc");
        audioPlayer.play("avi", "mind me.avi");
    }
}


/**
 * 为媒体播放器创建接口 mp3
 */
interface MediaPlayer {

    void play(String audioType, String fileName);
}

/**
 * 为更高级的媒体播放器创建接口。 多媒体
 */
interface AdvancedMediaPlayer {

    void playVlc(String fileName);

    void playMp4(String fileName);
}


/**
 * 创建实现了 AdvancedMediaPlayer 接口的实体类。
 * Vlc实现类  目标类
 */
class VlcPlayer implements AdvancedMediaPlayer {
    @Override
    public void playVlc(String fileName) {
        System.out.println("Playing vlc file. Name: " + fileName);
    }

    @Override
    public void playMp4(String fileName) {
        // 什么也不做
    }
}

/**
 * 创建实现了 AdvancedMediaPlayer 接口的实体类。
 * Mp4实现类  目标类
 */
class Mp4Player implements AdvancedMediaPlayer {

    @Override
    public void playVlc(String fileName) {
        // 什么也不做
    }

    @Override
    public void playMp4(String fileName) {
        System.out.println("Playing mp4 file. Name: " + fileName);
    }
}

/**
 * 创建实现了 MediaPlayer 接口的适配器类。
 * 适配器类--实现A接口，并创建B接口的实例，这样就具备了A和B共同的功能
 */
class MediaAdapter implements MediaPlayer {

    AdvancedMediaPlayer advancedMusicPlayer;

    public MediaAdapter(String audioType) {
        if (audioType.equalsIgnoreCase("vlc")) {
            advancedMusicPlayer = new VlcPlayer();
        } else if (audioType.equalsIgnoreCase("mp4")) {
            advancedMusicPlayer = new Mp4Player();
        }
    }

    @Override
    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("vlc")) {
            advancedMusicPlayer.playVlc(fileName);
        } else if (audioType.equalsIgnoreCase("mp4")) {
            advancedMusicPlayer.playMp4(fileName);
        }
    }
}

/**
 * 创建实现了 MediaPlayer 接口的实体类。
 * 适配者类，只要创建适配器对象，
 */
class AudioPlayer implements MediaPlayer {

    //适配器类
    MediaAdapter mediaAdapter;

    @Override
    public void play(String audioType, String fileName) {
        //播放 mp3 音乐文件的内置支持
        if (audioType.equalsIgnoreCase("mp3")) {
            System.out.println("Playing mp3 file. Name: " + fileName);
        }
        //mediaAdapter 提供了播放其他文件格式的支持
        else if (audioType.equalsIgnoreCase("vlc")
                || audioType.equalsIgnoreCase("mp4")) {
            mediaAdapter = new MediaAdapter(audioType);
            mediaAdapter.play(audioType, fileName);
        } else {
            System.out.println("Invalid media. " +
                    audioType + " format not supported");
        }
    }
}

/****************************其他实例*******************************************************/
